package pnu.ssm.genetic.algorithm;

public class Algorithm {
	// GA parameters
	private static final double uniformRate = 0.5;
	private static final double mutationRate = 0.015;
	private static final int tournamentSize = 5;
	private static final boolean elitism = true;
	
	// Public methods
	// Evolve a population
	public static Population evolvePopulation(Population pop){
		// GA의 핵심 기능인 선택(Selection), 교차(Crossover)와 변이(Mutation)가 존재한다.
		// pop은 이 전 세대이고 newPop은 새로운 세대이다.
		Population newPopulation =  new Population(pop.size(), false);
		// 새로운 세대를 만들어낸다. 아직 모든 염색체가 채워지지 않았다.
		
		// Keep out best individual
		if(elitism){
			// elitism은 전 세대(Population)의 최고 좋은 녀석을 남겨놓겠다는 의미다.
			newPopulation.saveIndividual(0, pop.getFittest());
		}
		
		// Crossover population
		int elitismOffset = elitism ? 1 : 0;
		
		// Loop over the population size and create new individuals with crossover
		for(int i=elitismOffset; i<pop.size(); i++){
			Individual indiv1 = tournamentSelection(pop); 
			Individual indiv2 = tournamentSelection(pop);
			Individual newIndiv = crossover(indiv1, indiv2);
			// 이전 세대의 토너먼트(경쟁)를 통해 골라낸 5개의 염색체 중에 가장 좋은 녀석 둘을 교차(Crossover) 한다. 
			newPopulation.saveIndividual(i,newIndiv);
			// 이전 세대에서 교차된 염색체를 새로운 세대의 염색체로 넣는다. 
			// 이 Loop 를 통해 새로운 세대의 염색체를 모두 채워넣는다.
		}
		
		// Mutate population
		for(int i = elitismOffset; i<newPopulation.size(); i++){
			mutate(newPopulation.getIndividual(i));
		}
		
		return newPopulation;
	}
	
	// Crossover individuals
	private static Individual crossover(Individual indiv1, Individual indiv2){
		Individual newSol = new Individual();
		//Loop through genes
		for(int i=0; i<indiv1.size(); i++){
			//Crossover
			if(Math.random() <= uniformRate){
				newSol.setGene(i, indiv1.getGene(i));
			} else {
				newSol.setGene(i, indiv2.getGene(i));
			}
		}
		return newSol;
	}
	
	// Select an individual
	private static void mutate (Individual indiv){
		// 변인 연산 방법 중 전형적 범위를 위한 메서드다. 
		// Population 들이 염색체가 indiv로 들어온다.
		// 새로운 세대를 받아서 돌연변이를 만들어낸다. 돌연변이는 랜덤하게 만들어진다.
		// Loop through genes
		for(int i=0; i<indiv.size(); i++){
			if(Math.random() <= mutationRate){
				byte gene = (byte) Math.round(Math.random());
				indiv.setGene(i, gene);
			}
		}
	}
	
	// Select individuals for crossover
	private static Individual tournamentSelection(Population pop){
		// 선택 알고리즘 중 하나인 토너먼트 선택을 위한 메서드다.
		// Create a tournament population
		Population tournament = new Population(tournamentSize, false);
		// For each place in the tournament get a random individual
		for(int i=0; i<tournamentSize; i++){
			int randomId = (int) (Math.random() * pop.size());
			tournament.saveIndividual(i, pop.getIndividual(randomId));
			// 이전 세대 (Population)에서 tournamentSize 만큼 골라낸다. 
		}
		// Get the fittest
		Individual fittest = tournament.getFittest();
		// 골라낸 염색체들 (individuals) 중에 가장 높은 염색체를 고른다.
		return fittest;
	}
}







